#ifndef __ARCH_PGD_H__
#define __ARCH_PGD_H__

#include "kernel/page.h"

#ifdef __cplusplus
extern "C" {
#endif

#define PGF_PRESENT					(1)
#define PGF_RW						(2)	// read = 0, write = 1
#define PGF_US						(4)	// kernel = 0, user = 1

// bit 1 of AVL (1 of 3)
#define PFG_ACCESSOR_LOCK			(0x200)

#define PAGE_PRESENT(v)				((v) & PGF_PRESENT)

#define GET_OFFSET(v)				((v) >> 22)
#define GET_PGD_OFFSET(v)			GET_OFFSET(v)
#define GET_PTE_OFFSET(v)			(((v) & 0x3FF000) >> 12)

#define PGD_GET_IDX(pgd)			((pgd) >> PAGE_SHIFT)
#define PTE_GET_IDX(pte)			((pte) >> PAGE_SHIFT)

// size of low memory area
#define SZ_MEM_LOW_AREA				(16 * 1024 * 1024)				// 16MB

struct page;
struct mem_reserved_area
{
	uint start;
	uint end;
};

// describe the reserved area
extern struct mem_reserved_area sys_mem_resv_area[];

// the page root dir (1 page)
extern uint* page_root_dir;

// the pte start addr for accessor area
extern uint* page_accessor_pte_start_addr;

// for accessor
uint flat_get_accessor_pgd_count(void);

// init the accessor area
void init_accessor_area(void);

// get the phyical page mapped for current
// virtual address (NULL returned while not mapped)
// note: this function is not locked
struct page* _virt_addr_page(uint virtaddr);

// after this call, the phy page is able to be accessed
// via the virtual address stored in page_t
uint page_accessor_bind_phy_page(struct page *p, bool lock);

// try lock a page of accessor area
// this may not success if the map to the page
// was destroyed
bool page_accessor_lock(uint virt_addr);

// unlock a page of accessor area
void page_accessor_unlock(uint virt_addr);

// map a page to virtual addr
int map_page(uint* pgdt, uint virt_addr, struct page *p, uint flags);

// map a page for kernel
#define krnl_map_page(va, p, f)	map_page((uint *)page_root_dir, va, p, f)

// page dir/table entry operation
#define _set_pgd(pgd, idx, pgidx, flag)		\
	do { ((uint*)(pgd))[idx] = (pgidx << PAGE_SHIFT) | (flag) | PGF_PRESENT; } while (0)

// this is used only for kernel pgd update
int set_kernel_pgd(uint idx, uint pgidx, uint flag);

// link a task specific page dir into the list
bool link_task_pgd(uint* tsk_pgd);

#define _set_pte(pte, idx, pgidx, flag)		\
	do {	\
		uint lock = ((uint*)(pte))[idx] & PFG_ACCESSOR_LOCK;	\
		((uint*)(pte))[idx] = (pgidx << PAGE_SHIFT) | (flag) | lock | PGF_PRESENT;	\
	} while (0)

#ifdef __cplusplus
}
#endif
#endif
/* EOF */
